//=============================================================================
///	Type definition
//=============================================================================

#ifndef	__STYPE_H__
#define	__STYPE_H__

#include "config.h"
#include "hashtable.h"
#include <limits.h>		// for CHAR_BIT

#ifdef __cplusplus
extern "C" {
#endif

/// Fixnum: need same size for pointer
typedef int SFixnum;

#define	FIXNUM_MAX		((1L<<(sizeof(SFixnum) * CHAR_BIT -1-1))-1)
#define	FIXNUM_MIN		(-(1L<<(sizeof(SFixnum) * CHAR_BIT -1-1)))

/// Floating-point number
typedef double SFPnum;


/// Type
typedef enum {
	tUnknown = -1,			///< Unknown
	tFixnum,				///< Fixed size integer
	tChar,					///< Char
	tCell,					///< Cell
	tSymbol,				///< Symbol
	tString,				///< String
	tStream,				///< Stream
	tSpecialForm,			///< Special form
	tSubr,					///< C function
	tLambda,				///< Lisp function
	tMacro,					///< Lisp macro
	tHash,					///< Hash table
	tFPnum,					///< Floating-point number
} SType;

/// S-expression
typedef union SExp {
	SFixnum i;				///< Fixnum
	union SExpPtr* ptr;		///< Pointer
} SExp;


typedef struct {
	short type;
	short flag;
	const char* class_name;
	const int* sexp_ofstbl;		///< offset table of structure's inner sexp members, [-1]=end
	void (*term)(SExp s);
	void (*mark)(SExp s);
	void (*write)(SExp s, SExp strm, int escape);
	SExp (*apply)(SExp fn, SExp args, int not_evaled);
	SExp (*macroexpand)(SExp self, SExp s);
} STypeInfo;

// Common header
#define	SEXPEXTHDR	\
	const STypeInfo* typeinfo;

/// Cell
typedef struct {
	SEXPEXTHDR
	SExp car;
	SExp cdr;
} Cell;

/// Symbol
typedef struct {
	SEXPEXTHDR
	char str[1];
} Symbol;

/// String
typedef struct {
	SEXPEXTHDR
	int len;
	char str[1];
} String;

/// Stream
typedef struct {
	SEXPEXTHDR
	struct StreamBaseVTbl* vtbl;
	SExp fn;			///< File name
	int lineno;			///< Line number
	int colno;			///< Column number
} Stream;
	// Function table for Stream
struct StreamBaseVTbl {
	void (*close)(Stream* strm);
	int (*getch)(Stream* strm);
	void (*ungetch)(Stream* strm, int c);
	int (*write)(Stream* strm, const char* str);
};

/// Special form
typedef SExp (*SpecialFormType)(SExp args, int macroexpand);
typedef struct {
	SEXPEXTHDR
	SpecialFormType cfunc;
	const char* name;
	short minargnum, maxargnum;
} SpecialForm;

/// C function
typedef SExp (*SubrType)(int narg, SExp args[]);
typedef struct {
	SEXPEXTHDR
	SubrType cfunc;
	const char* name;
	short minargnum, maxargnum;
} Subr;

/// Lambda
typedef struct {
	SEXPEXTHDR
	SExp name;
	SExp arglist;
	SExp body;
	short minargnum, maxargnum;
	SExp lexical_env;
} Lambda;

/// Macro
typedef struct {
	SEXPEXTHDR
	SExp name;
	SExp arglist;
	SExp body;
	short minargnum, maxargnum;
	SExp lexical_env;
} Macro;





/// Hash
typedef struct {
	SEXPEXTHDR
	HashTable ht;
} SHash;

/// Floating-point
typedef struct {
	SEXPEXTHDR
	SFPnum val;
} SFPoint;






#define	SUBTYPE_BITS		(6)
#define	SUBTYPE_MASK		(0xfc)
#define	GET_SUBTYPE(x)		((x) & SUBTYPE_MASK)
#define	GET_SUBVALUE(x)		((x) >> 8)

#define	SUBTYPE_UNKNOWN		(0x00 << 2)
#define	SUBTYPE_CHAR		(0x01 << 2)

INLINE SExp MAKE_SPECIAL(int subtype, int n)	{ SExp s; s.i = (n<<(SUBTYPE_BITS + 2))|(subtype)|2; return s; }

#define	sEOF				MAKE_SPECIAL(SUBTYPE_UNKNOWN, -1)
#define	sUNDEF				MAKE_SPECIAL(SUBTYPE_UNKNOWN, -2)
#define	sERR				MAKE_SPECIAL(SUBTYPE_UNKNOWN, -3)

typedef union SExpPtr {
	struct {
		SEXPEXTHDR
	} hdr;
	Cell cell;
	Symbol symbol;
	String string;
	Stream stream;
	SpecialForm specialform;
	Subr subr;
	Lambda lambda;
	Macro macro;
	SHash hash;
	SFPoint fpnum;
} SExpPtr;




/// ReadCell
typedef struct {
	Cell base;
	SExp filename;
	int lineno;
	int colno;				///< column number
} ReadCell;


#ifdef __cplusplus
} // extern "C"
#endif

#endif
